package org.liurb.springboot.starter.web.logger.aspect;

import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.liurb.springboot.starter.common.utils.RequestHttpUtil;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


import java.util.Arrays;

/**
 * 请求日志切面处理
 *
 * @Author Liurb
 * @Date 2022/11/28
 */
@Aspect
@Component
@Slf4j
@Order(10)
public class WebLogAspect {

    /**
     * 标记
     */
    private String requestId;
    /**
     * 进入方法时间戳
     */
    private Long startTime;
    /**
     * 方法结束时间戳(计时)
     */
    private Long endTime;

    public WebLogAspect() {
    }


    /**
     * 定义请求日志切入点，其切入点表达式有多种匹配方式,这里是指定路径
     */
    @Pointcut("execution(public * org.liurb..*.controller..*Controller.*(..))")
    public void webLogPointcut() {
    }

    /**
     * 前置通知：
     * 1. 在执行目标方法之前执行，比如请求接口之前的登录验证;
     * 2. 在前置通知中设置请求日志信息，如开始时间，请求参数，注解内容等
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLogPointcut()")
    public void doBefore(JoinPoint joinPoint) {

        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //创建标记
        requestId = request.getHeader("backend-request-id");
        if (StrUtil.isBlank(requestId)) {
            requestId = StrUtil.uuid().replace("-","").toUpperCase();
        }
        //打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("{} 请求Url : {}", requestId, request.getRequestURL().toString());
        String userAgent = request.getHeader(HttpHeaders.USER_AGENT);
        log.info("{} 请求UA : {}", requestId, userAgent);
        log.info("{} 请求ip : {}", requestId, RequestHttpUtil.getIpAddress(request));
        log.info("{} 请求方法 : {}", requestId, joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("{} 请求参数 : {}", requestId, Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 返回通知：
     * 1. 在目标方法正常结束之后执行
     * 1. 在返回通知中补充请求日志信息，如返回时间，方法耗时，返回值，并且保存日志信息
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
    public void doAfterReturning(Object ret) {
        endTime = System.currentTimeMillis();
        log.info("{} 请求耗时：{}", requestId, (endTime - startTime) + "ms");
        // 处理完请求，返回内容
        log.info("{} 请求返回 : {}", requestId, ret);
    }

    /**
     * 异常通知：
     * 1. 在目标方法非正常结束，发生异常或者抛出异常时执行
     * 1. 在异常通知中设置异常信息，并将其保存
     *
     * @param throwable
     */
    @AfterThrowing(value = "webLogPointcut()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        // 打印异常日志记录
        log.error("{} 抛出异常：{}", requestId, throwable.getMessage(), throwable);
    }

}
